﻿#pragma once

#include <functional>
#include <future>
#include <iostream>

#include <xy/utils/mtqueue.hpp>

namespace xy
{

namespace utils
{

class job_system
{
  private:
    // 任务队列，由于在主线程、worker 线程之间共享，需要使用 mt_queue
    inline static std::thread::id mainThreadId{std::this_thread::get_id()};
    inline static std::atomic_int waiters{0};
    inline static std::atomic<bool> running{false};
    inline static mt_queue<std::function<void()>> jobs{};

  public:
    static void init()
    {
        if (running.load())
            return;

        running.store(true);
        std::thread{[] {
            while (running)
            {
                if (auto job = jobs.try_pop())
                    std::thread(job.value()).detach();
                else
                    std::this_thread::yield();
            }
        }}.detach();
    }

    static void dispatch(std::function<void()> job)
    {
        if (!running)
            throw std::runtime_error("job_system is not running");

        waiters.fetch_add(1);
        jobs.push([job = std::move(job)]() {
            job();
            waiters.fetch_sub(1);
        });
    }

    static void wait()
    {
        if (std::this_thread::get_id() != mainThreadId)
            throw std::runtime_error("wait() can only be called from the main thread");

        while (waiters.load() > 0)
            std::this_thread::yield();
    }

    static void shutdown()
    {
        running.store(false);
    }

    static bool is_running()
    {
        return running.load();
    }
};

} // namespace utils

} // namespace xy
